﻿namespace MicroCloud.AspNetCore.Mvc.Filters
{
    /// <summary>
    /// 自动事务提交过滤器，在<see cref="ActionFilterAttribute.OnResultExecuted"/>方法中执行<see cref="IUnitOfWork.Commit()"/>进行事务提交
    /// </summary>
    internal class UnitOfWorkImpl : IActionFilter
    {
        //字段
        private readonly IServiceProvider _provider;
        private readonly IUnitOfWork _unitOfWork;
        private readonly ILogger _logger;

        #region "构造函数"
        /// <summary>
        /// 自动事务提交过滤器，在<see cref="ActionFilterAttribute.OnResultExecuted"/>方法中执行<see cref="IUnitOfWork.Commit()"/>进行事务提交
        /// </summary>
        /// <param name="provider"></param>
        public UnitOfWorkImpl(IServiceProvider provider)
        {
            _provider = provider;
            _unitOfWork = provider.GetUnitOfWork(true);
            _logger = provider.GetLogger<UnitOfWorkImpl>();
        }

        #endregion

        /// <summary>
        /// 作用于Action执行时
        /// <para>Called before the action executes, after model binding is complete.</para>
        /// </summary>
        /// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ActionExecutingContext" />.</param>
        public void OnActionExecuting(ActionExecutingContext context)
        {
            var unitOfWorkAttr = context?.ActionDescriptor?.EndpointMetadata?.OfType<UnitOfWorkAttribute>().FirstOrDefault();
            if (unitOfWorkAttr != null)
            {
                var immediate = unitOfWorkAttr.Immediate;
                var isolationLevel = unitOfWorkAttr.IsolationLevel;
                ScopedDictionary dict = _provider.GetService<ScopedDictionary>();
                if (dict != null)
                {
                    dict.IsolationLevel = isolationLevel;
                }
                if (immediate)
                {
                    _unitOfWork.BeginOrUseTransaction(isolationLevel);
                }
            }
        }

        /// <summary>
        /// 作用于ActionResult执行完成后：
        /// Called after the action executes, before the action result.
        /// </summary>
        /// <param name="context">The <see cref="T:Microsoft.AspNetCore.Mvc.Filters.ActionExecutedContext" />.</param>
        public void OnActionExecuted(ActionExecutedContext context)
        {
            ScopedDictionary dict = _provider.GetService<ScopedDictionary>();
            AjaxResultType type = AjaxResultType.Success;
            string message = null;
            if (context.Exception != null && !context.ExceptionHandled)
            {
                System.Exception ex = context.Exception;
                _logger.LogError(new EventId(), ex, ex.Message);
                message = ex.Message;
                if (context.HttpContext.Request.IsAjaxRequest() || context.HttpContext.Request.IsJsonContextType())
                {
                    if (!context.HttpContext.Response.HasStarted)
                    {
                        context.Result = new JsonResult(AjaxResult.Error(ex.Message));
                    }
                    context.ExceptionHandled = true;
                }
            }
            if (context.Result is JsonResult result1)
            {
                if (result1.Value is AjaxResult ajax)
                {
                    type = ajax.Type;
                    message = ajax.Content;
                    if (ajax.Succeeded)
                    {
                        _unitOfWork?.Commit();
                    }
                }
            }
            else if (context.Result is ObjectResult result2)
            {
                if (result2.Value is AjaxResult ajax)
                {
                    type = ajax.Type;
                    message = ajax.Content;
                    if (ajax.Succeeded)
                    {
                        _unitOfWork?.Commit();
                    }
                }
                else
                {
                    _unitOfWork?.Commit();
                }
            }
            //普通请求
            else if (context.HttpContext.Response.StatusCode >= 400)
            {
                type = context.HttpContext.Response.StatusCode switch
                {
                    401 => AjaxResultType.Unauthorized,
                    403 => AjaxResultType.Unauthorized,
                    404 => AjaxResultType.Unauthorized,
                    423 => AjaxResultType.Unauthorized,
                    _ => AjaxResultType.Error,
                };
            }
            else
            {
                type = AjaxResultType.Success;
                _unitOfWork?.Commit();
            }

            if (dict.AuditOperation != null)
            {
                dict.AuditOperation.ResultType = type;
                dict.AuditOperation.Message = message;
            }
        }

    }
}
